#include <gtest\gtest.h>
#include <gmock\gmock.h>
#include <boost\shared_ptr.hpp>
#include <string>


#include <connection\messages_id.h>
#include <connection\message_proto_typedef.h>
#include <connection\registry.h>
#include <connection\dispatcher.h>

#include "..\mock\session_mock.h"

namespace cs {

namespace constant
{
	const std::string service_name("dummy_service");
	const std::string name_ind("my_addr");
	const address session_addr_1 = 1;
	const uint16_t message_id = 4444;
}

using ::testing::_;
using ::testing::SaveArg;

class ut_dispatcher : public ::testing::Test
{
protected:
	ut_dispatcher()
	{
		session_mock_1 = new session_mock();
		session_mock_2 = new session_mock();
		session_mock_3 = new session_mock();

		session_ptr_1.reset(session_mock_1);
		session_ptr_2.reset(session_mock_2);
		session_ptr_3.reset(session_mock_3);
		
		message service_addr;
		EXPECT_CALL(*session_mock_1, deliver(_)).WillOnce(SaveArg<0>(&service_addr));
		
		dispatcher.reset(new cs::dispatcher(session_ptr_1));
		
		service_address_proto proto(service_addr.get());
		EXPECT_EQ(proto->service_name(), constant::name_ind);
		EXPECT_EQ(proto->address(), constant::session_addr_1);
	}

	void check_session_addr()
	{
	}

	~ut_dispatcher()
	{
		service_registry::instance().remove_session(session_ptr_1);
		service_registry::instance().remove_session(session_ptr_2);
		service_registry::instance().remove_session(session_ptr_3);

		service_registry::instance().clear_address_generator();
	}

	boost::shared_ptr<dispatcher> dispatcher;

	session_mock* session_mock_1;
	session_mock* session_mock_2;
	session_mock* session_mock_3;

	registry::session_ptr session_ptr_1;
	registry::session_ptr session_ptr_2;
	registry::session_ptr session_ptr_3;
	address session_addr_2;
	address session_addr_3;
};

TEST_F(ut_dispatcher, register_service)
{
	register_service_req_proto register_req;
	register_req->set_service_name(constant::service_name);

	message_handle req(new message(REGISTER_SERVICE_REQUEST, constant::session_addr_1, 0,
		register_req.serialize()));

	message resp;
	EXPECT_CALL(*session_mock_1, deliver(_))
		.WillOnce(SaveArg<0>(&resp))
		.WillOnce(SaveArg<0>(&resp));

	dispatcher->dispatch(req);

	EXPECT_EQ(register_service_resp_proto(resp.get())->status(),
		RegisterServiceResponse::SUCCESS);

	dispatcher->dispatch(req);

	EXPECT_EQ(register_service_resp_proto(resp.get())->status(),
		RegisterServiceResponse::REJECTED);
}

TEST_F(ut_dispatcher, subscribe_service_no_addr)
{
	subscribe_service_req_proto subscribe_proto;
	subscribe_proto->set_service_name(constant::service_name);
	message_handle subscribe(new message(SUBSCRIBE_SERVICE_REQUEST, constant::session_addr_1, 0,
		subscribe_proto.serialize()));

	message service_addr;
	EXPECT_CALL(*session_mock_1, deliver(_)).Times(0);
	dispatcher->dispatch(subscribe);
}

TEST_F(ut_dispatcher, subscribe_service_receive_address)
{
	session_addr_2 = service_registry::instance().add_session(session_ptr_2);
	service_registry::instance().register_service(session_ptr_2, constant::service_name);
	subscribe_service_req_proto subscribe_proto;
	subscribe_proto->set_service_name(constant::service_name);
	message_handle subscribe(new message(SUBSCRIBE_SERVICE_REQUEST, constant::session_addr_1, 0,
		subscribe_proto.serialize()));

	message service_addr;
	EXPECT_CALL(*session_mock_1, deliver(_)).WillOnce(SaveArg<0>(&service_addr));
	dispatcher->dispatch(subscribe);

	EXPECT_EQ(service_addr.receiver(), constant::session_addr_1);

	service_address_proto proto(service_addr.get());
	EXPECT_EQ(proto->service_name(), constant::service_name);
	EXPECT_EQ(proto->address(), session_addr_2);
}

TEST_F(ut_dispatcher, subscribers_receive_registered_service_address)
{
	session_addr_2 = service_registry::instance().add_session(session_ptr_2);
	session_addr_3 = service_registry::instance().add_session(session_ptr_3);

	service_registry::instance().subscribe_sevice(session_ptr_2, constant::service_name);
	service_registry::instance().subscribe_sevice(session_ptr_3, constant::service_name);

	register_service_req_proto register_req;
	register_req->set_service_name(constant::service_name);

	message_handle req(new message(REGISTER_SERVICE_REQUEST, constant::session_addr_1, 0,
		register_req.serialize()));
	
	message msg_addr_1, msg_addr_2;
	message resp;

	EXPECT_CALL(*session_mock_1, deliver(_)).WillOnce(SaveArg<0>(&resp));
	EXPECT_CALL(*session_mock_2, deliver(_)).WillOnce(SaveArg<0>(&msg_addr_1));
	EXPECT_CALL(*session_mock_3, deliver(_)).WillOnce(SaveArg<0>(&msg_addr_2));

	dispatcher->dispatch(req);
	
	EXPECT_EQ(register_service_resp_proto(resp.get())->status(),
		RegisterServiceResponse::SUCCESS);

	EXPECT_EQ(msg_addr_1.id(), SERVICE_ADDRESS);
	EXPECT_EQ(msg_addr_2.id(), SERVICE_ADDRESS);
	EXPECT_EQ(msg_addr_2.sender(), 0);
	EXPECT_EQ(msg_addr_2.sender(), 0);
	EXPECT_EQ(msg_addr_1.receiver(), session_addr_2);
	EXPECT_EQ(msg_addr_2.receiver(), session_addr_3);

	service_address_proto proto_1(msg_addr_1.get());
	service_address_proto proto_2(msg_addr_2.get());

	EXPECT_EQ(proto_1->service_name(), constant::service_name);
	EXPECT_EQ(proto_2->service_name(), constant::service_name);
	EXPECT_EQ(proto_1->address(), constant::session_addr_1);
	EXPECT_EQ(proto_2->address(), constant::session_addr_1);
}

TEST_F(ut_dispatcher, forward_message)
{
	session_addr_2 = service_registry::instance().add_session(session_ptr_2);
	message_handle msg(new message(constant::message_id, constant::session_addr_1, session_addr_2));
	message forwarded;

	EXPECT_CALL(*session_mock_2, deliver(_)).WillOnce(SaveArg<0>(&forwarded));
	dispatcher->dispatch(msg);

	EXPECT_EQ(forwarded.id(), msg->id());
	EXPECT_EQ(forwarded.sender(), msg->sender());
	EXPECT_EQ(forwarded.receiver(), msg->receiver());
}

}